namespace SimpleWebServices.Web.UI.Html.Elements
{
    using System;
    using System.Collections.ObjectModel;
    using System.Diagnostics.CodeAnalysis;
    using System.Linq;
    using System.Xml;
    using System.Xml.Serialization;

    [SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Select", Justification = "The naming is intentionally the same as HTML.")]
    [XmlRoot("select")]
    public class Select : Control, IXmlSerializable
    {
        public Select()
        {
            this.Options = new Collection<IOption>();
        }

        public Select(Name id)
            : base(id)
        {
            this.Options = new Collection<IOption>();
        }

        public bool MultipleSelections
        {
            get;
            set;
        }

        public Collection<IOption> Options
        {
            get;
            private set;
        }

        public int? RowsVisible
        {
            get;
            set;
        }

        public override string Value
        {
            get
            {
                string value = null;

                Option option = null;
                OptionGroup group = null;
                foreach (IOption item in this.Options)
                {
                    option = item as Option;
                    if (null != option && option.Selected)
                    {
                        value = option.Value;
                        break;
                    }

                    group = item as OptionGroup;
                    if (null != group)
                    {
                        foreach (Option child in group.Options.Where(x => null != x && x.Selected))
                        {
                            value = child.Value;
                            break;
                        }
                    }

                    if (null != value)
                    {
                        break;
                    }
                }

                return value;
            }

            set
            {
                Option option = null;
                OptionGroup group = null;
                foreach (IOption item in this.Options)
                {
                    option = item as Option;
                    if (null != option)
                    {
                        option.Selected = 0 == string.Compare(value, option.Value, StringComparison.OrdinalIgnoreCase);
                    }

                    group = item as OptionGroup;
                    if (null != group)
                    {
                        foreach (Option child in group.Options.Where(x => null != x))
                        {
                            child.Selected = 0 == string.Compare(value, child.Value, StringComparison.OrdinalIgnoreCase);
                        }
                    }
                }
            }
        }

        public bool Contains(string value)
        {
            bool result = false;

            if (null != value)
            {
                Option option = null;
                OptionGroup group = null;
                foreach (IOption item in this.Options)
                {
                    option = item as Option;
                    if (null != option && value.Equals(option.Value, StringComparison.OrdinalIgnoreCase))
                    {
                        result = true;
                        break;
                    }

                    group = item as OptionGroup;
                    if (null != group)
                    {
                        result = null != group.Options
                            .Where(x => null != x)
                            .Where(x => value.Equals(x.Value, StringComparison.OrdinalIgnoreCase))
                            .FirstOrDefault();
                    }

                    if (result)
                    {
                        break;
                    }
                }
            }

            return result;
        }

        public override IElement Deserialize(string name, string type, string xml)
        {
            IElement result = null;

            if (null == type && "select".Equals(name, StringComparison.OrdinalIgnoreCase))
            {
                result = xml.Deserialize<Select>();
            }

            return result;
        }

        public override void ReadXml(XmlReader reader)
        {
            base.ReadXml(reader);

            string value = reader.GetAttribute("size");
            if (!string.IsNullOrEmpty(value))
            {
                this.RowsVisible = XmlConvert.ToInt32(value);
            }

            this.MultipleSelections = "multiple".Equals(reader.GetAttribute("multiple"), StringComparison.OrdinalIgnoreCase);

            if (reader.IsEmptyElement)
            {
                reader.Read();
            }
            else
            {
                string name = reader.Name;
                reader.Read();
                while (true)
                {
                    if (XmlNodeType.None == reader.NodeType)
                    {
                        break;
                    }
                    else if (XmlNodeType.EndElement == reader.NodeType && reader.Name == name)
                    {
                        reader.Read();
                        break;
                    }
                    else if ("option".Equals(reader.Name, StringComparison.OrdinalIgnoreCase))
                    {
                        this.Options.Add(reader.ReadOuterXml().Deserialize<Option>());
                    }
                    else if ("optgroup".Equals(reader.Name, StringComparison.OrdinalIgnoreCase))
                    {
                        this.Options.Add(reader.ReadOuterXml().Deserialize<OptionGroup>());
                    }
                    else
                    {
                        reader.Read();
                    }
                }
            }
        }

        public override void WriteXml(XmlWriter writer)
        {
            base.WriteXml(writer);

            if (this.MultipleSelections)
            {
                writer.WriteAttributeString("multiple", "multiple");
            }

            if (this.RowsVisible.HasValue)
            {
                writer.WriteAttributeString("size", XmlConvert.ToString(this.RowsVisible.Value));
            }

            foreach (IOption option in this.Options)
            {
                writer.WriteRaw(option.ToString());
            }
        }
    }
}